#pragma once

#include <iostream>
#include <vector>

#include "Sem.hpp"
#include "Mutex.hpp"

// RingQueue here aka RingBuffer
// v2 multi-producer & consumer equiped with my mutex
namespace RingQueueModule
{
    using namespace LockModule;
    using namespace SemaphoreModule;
    template <typename T>
    class RingQueue
    {
    public:
        RingQueue(int cap)
            : _ring_buffer(cap), _cap(cap), _p_step(0), _c_step(0), _datasem(0), _spacesem(cap)
        {
        }
        ~RingQueue()
        {
        }
        void Enqueue(const T &in)
        {
            // 生产者
            _spacesem.P();

            {
                /*生产者之间的锁放在申请空间内，使得P（申请）
                 *操作在多个生产者之间可以并发进行
                 **/
                LockGuard lockguard(_p_mutex);
                _ring_buffer[_p_step] = in;
                ++_p_step;
                _p_step %= _cap;
            }

            _datasem.V();
        }
        void Dequeue(T *out)
        {
            _datasem.P();
            {
                LockGuard lockguard(_c_mutex);
                *out = _ring_buffer[_c_step];
                ++_c_step;
                _c_step %= _cap;
            }
            _spacesem.V();
        }

    private:
        std::vector<T> _ring_buffer;
        int _cap;
        int _p_step;
        int _c_step;
        Sem _datasem;
        Sem _spacesem;

        Mutex _p_mutex; // 多个生产者之间竞争这把锁 最后产出一个结果（也就是信号量为1
        Mutex _c_mutex; // 同上
    };

}

// v1 single consumer producer
// namespace RingQueueModule
// {
//     using namespace SemaphoreModule;
//     template<typename T>
//     class RingQueue
//     {
//     public:
//         RingQueue(int cap)
//             : _ring_buffer(cap)
//             , _cap(cap) , _p_step(0), _c_step(0)
//             , _datasem(0), _spacesem(cap)
//         {}
//         ~RingQueue()
//         {}
//         void Enqueue(const T &in)
//         {
//             _spacesem.P();
//             _ring_buffer[_p_step] = in;
//             ++_p_step;
//             _p_step %= _cap;
//             _datasem.V();
//         }
//         void Dequeue(T* out)
//         {
//             _datasem.P();
//             *out = _ring_buffer[_c_step];
//             _c_step++;
//             _c_step %= _cap;
//             _spacesem.V();
//         }
//     private:
//         std::vector<T> _ring_buffer;
//         int _cap;
//         int _p_step;
//         int _c_step;
//         Sem _datasem;
//         Sem _spacesem;
//     };

// }